#include "stdlib.hpp"
#include <purewell/file.hpp>
#include <purewell/system.hpp>

#include "libfile.hpp"

#ifdef WIN32
#	pragma warning(push)
#	pragma warning(disable: 4996)
#endif//WIN32

namespace purewell
{

PWFileFlag::PWFileFlag()
: m_flags(0)
{
}

PWFileFlag::PWFileFlag(int flags)
: m_flags(flags)
{
}

PWFileFlag::PWFileFlag(const PWFileFlag& obj)
: m_flags(obj.m_flags)
{
}

PWFileFlag::~PWFileFlag()
{
}

int
PWFileFlag::getFlags(void) const
{
	return m_flags;
}

int
PWFileFlag::setFlags(int flags)
{
	m_flags = flags;
	return m_flags;
}

bool
PWFileFlag::setCreate(bool v)
{
	return __PWSetBoolean(m_flags, O_CREAT, v);
}

bool
PWFileFlag::isCreate(void) const
{
	return __PWGetBoolean(m_flags, O_CREAT);
}

bool
PWFileFlag::setExclusive(bool v)
{
	return __PWSetBoolean(m_flags, O_EXCL, v);
}

bool
PWFileFlag::isExclusive(void) const
{
	return __PWGetBoolean(m_flags, O_EXCL);
}

bool
PWFileFlag::setTruncate(bool v)
{
	return __PWSetBoolean(m_flags, O_TRUNC, v);
}

bool
PWFileFlag::isTruncate(void) const
{
	return __PWGetBoolean(m_flags, O_TRUNC);
}

bool
PWFileFlag::setAppend(bool v)
{
	return __PWSetBoolean(m_flags, O_APPEND, v);
}

bool
PWFileFlag::isAppend(void) const
{
	return __PWGetBoolean(m_flags, O_APPEND);
}

bool
PWFileFlag::setNonblock(bool v)
{
#ifdef O_NONBLOCK
	return __PWSetBoolean(m_flags, O_NONBLOCK, v);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFileFlag::isNonblock(void) const
{
#ifdef O_NONBLOCK
	return __PWGetBoolean(m_flags, O_NONBLOCK);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFileFlag::setSync(bool v)
{
#ifdef O_SYNC
	return __PWSetBoolean(m_flags, O_SYNC, v);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFileFlag::isSync(void) const
{
#ifdef O_SYNC
	return __PWGetBoolean(m_flags, O_SYNC);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFileFlag::isReadOnly(void) const
{
	return ((m_flags & (O_WRONLY|O_RDWR)) == O_RDONLY);
}

bool
PWFileFlag::isWriteOnly(void) const
{
	return ((m_flags & O_WRONLY) == O_WRONLY);
}

bool
PWFileFlag::isReadAndWrite(void) const
{
	return ((m_flags & O_RDWR) == O_RDWR);
}

bool
PWFileFlag::setRWMode(int mode)
{
	if ( PWIO_R == mode )
	{
		m_flags &= ~(O_WRONLY|O_RDWR);
		m_flags |= O_RDONLY;
		return true;
	}
	else if ( PWIO_W == mode )
	{
		m_flags &= ~(O_RDONLY|O_RDWR);
		m_flags |= O_WRONLY;
		return true;
	}
	else
	{
		m_flags &= ~(O_RDONLY|O_WRONLY);
		m_flags |= O_RDWR;
		return true;
	}

	return true;
}

int
PWFileFlag::getRWMode(void) const
{
	if ( m_flags & O_RDWR ) return PWIO_R|PWIO_W;
	if ( m_flags & O_WRONLY ) return PWIO_W;
	return PWIO_R;
}

void
PWFileFlag::swap(PWFileFlag& obj)
{
	std::swap(m_flags, obj.m_flags);
}

PWFileFlag::operator int (void) const
{
	return m_flags;
}

PWFileFlag&
PWFileFlag::operator = (int flags)
{
	m_flags = flags;
	return *this;
}

PWFileFlag&
PWFileFlag::operator = (const PWFileFlag& flag)
{
	m_flags = flag.m_flags;
	return *this;
}

bool
PWFileFlag::operator == (int flags) const
{
	return m_flags == flags;
}

bool
PWFileFlag::operator == (const PWFileFlag& flags) const
{
	return m_flags == flags.m_flags;
}

bool
PWFileFlag::operator != (int flags) const
{
	return m_flags != flags;
}

bool
PWFileFlag::operator != (const PWFileFlag& flags) const
{
	return m_flags != flags.m_flags;
}

PWFileAccessMode::PWFileAccessMode()
:m_mode(0)
{
}

PWFileAccessMode::PWFileAccessMode(int mode)
:m_mode(mode)
{
}

PWFileAccessMode::PWFileAccessMode(const PWFileAccessMode& mode)
:m_mode(mode.m_mode)
{
}

PWFileAccessMode::~PWFileAccessMode()
{
}

int
PWFileAccessMode::getMode(void) const
{
	return m_mode;
}

int
PWFileAccessMode::setMode(int mode)
{
	m_mode = mode;
	return m_mode;
}

bool
PWFileAccessMode::setOwnerRead(bool v)
{
	return __PWSetBoolean(m_mode, 00400, v);
}

bool
PWFileAccessMode::isOwnerRead(void) const
{
	return __PWGetBoolean(m_mode, 00400);
}

bool
PWFileAccessMode::setOwnerWrite(bool v)
{
	return __PWSetBoolean(m_mode, 00200, v);
}

bool
PWFileAccessMode::isOwnerWrite(void) const
{
	return __PWGetBoolean(m_mode, 00200);
}

bool
PWFileAccessMode::setOwnerExecute(bool v)
{
	return __PWSetBoolean(m_mode, 00100, v);
}

bool
PWFileAccessMode::isOwnerExecute(void) const
{
	return __PWGetBoolean(m_mode, 00100);
}

bool
PWFileAccessMode::setGroupRead(bool v)
{
	return __PWSetBoolean(m_mode, 00040, v);
}

bool
PWFileAccessMode::isGroupRead(void) const
{
	return __PWGetBoolean(m_mode, 00040);
}

bool
PWFileAccessMode::setGroupWrite(bool v)
{
	return __PWSetBoolean(m_mode, 00020, v);
}

bool
PWFileAccessMode::isGroupWrite(void) const
{
	return __PWGetBoolean(m_mode, 00020);
}

bool
PWFileAccessMode::setGroupExecute(bool v)
{
	return __PWSetBoolean(m_mode, 00010, v);
}

bool
PWFileAccessMode::isGroupExecute(void) const
{
	return __PWGetBoolean(m_mode, 00010);
}

bool
PWFileAccessMode::setGuestRead(bool v)
{
	return __PWSetBoolean(m_mode, 00004, v);
}

bool
PWFileAccessMode::isGuestRead(void) const
{
	return __PWGetBoolean(m_mode, 00004);
}

bool
PWFileAccessMode::setGuestWrite(bool v)
{
	return __PWSetBoolean(m_mode, 00002, v);
}

bool
PWFileAccessMode::isGuestWrite(void) const
{
	return __PWGetBoolean(m_mode, 00002);
}

bool
PWFileAccessMode::setGuestExecute(bool v)
{
	return __PWSetBoolean(m_mode, 00001, v);
}

bool
PWFileAccessMode::isGuestExecute(void) const
{
	return __PWGetBoolean(m_mode, 00001);
}

PWFile::PWFile() : m_fd(-1)
{
}

PWFile::~PWFile()
{
	int fd(detach());
	if ( -1 != fd )
	{
#ifndef WIN32
		::close(fd);
#else
		::_close(fd);
#endif
	}
}

void
PWFile::onEvent(PWIOPoll* io, int fd, int event)
{
	fprintf(stderr, "PWFile::onEvent(%p, %d)\n",
		io, event);
}

void
PWFile::destroy(void)
{
	int fd(detach());
	if ( -1 != fd )
	{
#ifndef WIN32
		::close(fd);
#else
		::_close(fd);
#endif
	}
}

bool
PWFile::attach(int fd)
{
	m_fd = fd;
	return true;
}

int
PWFile::detach(void)
{
	int fd(m_fd);
	m_fd = -1;
	return fd;
}

ssize_t
PWFile::read(void* buf, size_t blen)
{
#ifndef WIN32
	return ::read(m_fd, (char*)buf, blen);
#else
	return ::_read(m_fd, (char*)buf, blen);
#endif
}

ssize_t
PWFile::write(const void* buf, size_t blen)
{
#ifndef WIN32
	return ::write(m_fd, (const char*)buf, blen);
#else
	return ::_write(m_fd, (const char*)buf, blen);
#endif
}

bool
PWFile::setModeBlock(bool block)
{
#ifdef O_NONBLOCK
	return __PWSetFlags(!block, m_fd, O_NONBLOCK);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFile::getModeBlock(bool& block) const
{
#ifdef O_NONBLOCK
	if ( !__PWGetFlags(block, m_fd, O_NONBLOCK) )
	{
		return false;
	}

	block = !block;
	return true;
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFile::setModeAsync(bool async)
{
#ifdef O_ASYNC
	return __PWSetFlags(async, m_fd, O_ASYNC);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFile::getModeAsync(bool& async) const
{
#ifdef O_ASYNC
	return __PWGetFlags(async, m_fd, O_ASYNC);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFile::setModeCloseOnExec(bool coe)
{
#ifdef FD_CLOEXEC
	return __PWSetFDFlags(coe, m_fd, FD_CLOEXEC);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFile::getModeCloseOnExec(bool& coe) const
{
#ifdef FD_CLOEXEC
	return __PWGetFDFlags(coe, m_fd, FD_CLOEXEC);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFile::getMode(int& mode) const
{
#ifdef __PW_HAVE_FCNTL
	int res(fcntl(m_fd, F_GETFL));
	if ( -1 == res )
	{
		return false;
	}

	mode = res;
	return true;
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFile::setMode(int mode)
{
#ifdef __PW_HAVE_FCNTL
	return 0 == fcntl(m_fd, F_SETFL, mode);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWFile::duplicate(int srcfd)
{
	int dfd(dup(srcfd));
	if ( -1 == dfd ) return false;

	bool res(attach(dfd));
	if ( false == res )
	{
		::close(dfd);
	}

	return res;
}

bool
PWFile::duplicate(int srcfd, int newfd)
{
	int dfd(dup2(srcfd, newfd));
	if ( -1 == dfd ) return false;

	bool res(attach(dfd));
	if ( false == res )
	{
		::close(dfd);
	}

	return res;
}

ssize_t
PWFile::readParallel(void* buf, size_t blen, off_t offset)
{
#if (_XOPEN_SOURCE - 0 ) >= 500
	return ( ::pread(m_fd, buf, blen, offset) );
#else
	__PWERRNO_NOIMPL();
	return -1;
#endif
}

ssize_t
PWFile::writeParallel(const void* buf, size_t blen, off_t offset)
{
#if (_XOPEN_SOURCE - 0 ) >= 500
	return ( ::pwrite(m_fd, buf, blen, offset) );
#else
	__PWERRNO_NOIMPL();
	return -1;
#endif
}

PWNamedFile::PWNamedFile()
{
}

PWNamedFile::~PWNamedFile()
{
}

bool
PWNamedFile::initialize(const char* name, int omode, int perm)
{
	std::string _name;
	PWSystem::getInstance().getCurrentDirectory(_name);
	if ( _name.empty() ) return false;
	_name.append(PWDIR_DELIMITER);
	_name.append(name);

	const int fd(open(name, omode, perm));
	if ( -1 == fd ) return false;

	m_name.swap(_name);
	m_fd = fd;

	return true;
}

const char*
PWNamedFile::getName(void) const
{
	return m_name.c_str();
}

bool
PWNamedFile::unlink(void)
{
	const bool res(0 == ::unlink(m_name.c_str()));
	if ( res )
	{
		m_name.clear();
	}

	return res;
}

int
PWNamedFile::detach(void)
{
	int fd(m_fd);
	m_name.clear();
	m_fd = -1;
	return fd;
}

};//namespace purewell

#ifdef WIN32
#	pragma warning(pop)
#endif//WIN32

